// ************************************************************
// Copyright © 2022
// File Name: packet.go
// Author:
// Email:
// Created: 2022-02-05 10:23:26 (CST)
// Last Update:
//          By:
// Description:
// ************************************************************
package packet

import (
	"encoding/binary"
	"fmt"
	"io"
)

type IMPackage struct {
	PackageLen  uint32 // 包长度
	HeaderLen   uint16 // 头长度
	ProtocalVer uint16 // 协议版本
	Operation   uint32 // 操作码
	SequenceID  uint64 // 请求序号
	Body        []byte // 包内容
}

type IMPackager interface {
	Decode(r io.Reader) error
	Encode(w io.Writer) error
}

const (
	Auth = iota + 0x1
	Heartbeat
	Message
)

func NewIMPackage() *IMPackage {
	return &IMPackage{
		HeaderLen: 20,
	}
}

func (p *IMPackage) Print() {
	formatString := "PackageLen: %d\nHeaderLen: %d\nProtocalVer: %d\nOperation: %d\nSequenceID: %d\nBody: %s\n"
	fmt.Printf(formatString,
		p.PackageLen,
		p.HeaderLen,
		p.ProtocalVer,
		p.Operation,
		p.SequenceID,
		string(p.Body))
}

func (p *IMPackage) Decode(r io.Reader) error {
	err := binary.Read(r, binary.BigEndian, &p.PackageLen)
	if err != nil {
		return err
	}

	buf := make([]byte, p.PackageLen-4)
	n, err := io.ReadFull(r, buf)

	if err != nil {
		return err
	}
	if n != int(p.PackageLen-4) {
		return err
	}

	p.HeaderLen = binary.BigEndian.Uint16(buf[0:2])
	p.ProtocalVer = binary.BigEndian.Uint16(buf[2:4])
	p.Operation = binary.BigEndian.Uint32(buf[4:8])
	p.SequenceID = binary.BigEndian.Uint64(buf[8:16])
	p.Body = buf[p.HeaderLen-4:]
	return nil
}

func (p *IMPackage) Encode(w io.Writer) error {
	err := binary.Write(w, binary.BigEndian, &p.PackageLen)
	if err != nil {
		return err
	}

	err = binary.Write(w, binary.BigEndian, &p.HeaderLen)
	if err != nil {
		return err
	}

	err = binary.Write(w, binary.BigEndian, &p.ProtocalVer)
	if err != nil {
		return err
	}

	err = binary.Write(w, binary.BigEndian, &p.Operation)
	if err != nil {
		return err
	}

	err = binary.Write(w, binary.BigEndian, &p.SequenceID)
	if err != nil {
		return err
	}
	n, err := w.Write(p.Body)
	if err != nil {
		return err
	}
	if n != len(p.Body) {
		return io.ErrShortWrite
	}
	return nil
}
